import { cmdInvoker } from '@amcax/base';
import {
  getFormIndex,
  nmAppErrorLog,
  nmAppLog,
} from '@app-nextmesh/common/utils';
import {
  IMenuItem,
  ITreeNode,
  TreeNodeType,
} from '@app-nextmesh/components/types';
import { useProjectStore } from '@app-nextmesh/store/project';
import { IComponent, LeftPanelEventType } from '@app-nextmesh/types';
import { events, updateGeometriesVisible } from '@app-nextmesh/utils';
import { useAsyncEffect } from 'ahooks';
import { isArray } from 'lodash';
import { useCallback, useEffect, useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { commandList } from '../../commands/commandList';
import { showLoading } from '../../components';
import { documentV2 } from '../../domain';
import { initLoad } from '../../pages/InitLoad';
import { useModelStore } from '../../store/model';
import { useShapeInfoStore } from '../../store/shapeinfo';
import { useSimulationsStore } from '../../store/simulations';
import { useTasksStore } from '../../store/tasks';
import Tree from './Tree';
import LeftTreeData, {
  toLocalCollections,
  toLocalComponent,
  toLocalMeshControls,
  toLocalResults,
  toLocalSimulation,
  toLocalTask,
  toLocalVisualizations,
} from './data';

const childParentTypeMap = {
  [TreeNodeType.Simulation]: TreeNodeType.Simulations,
  [TreeNodeType.Mesh]: TreeNodeType.Meshes,
  [TreeNodeType.Collections]: TreeNodeType.Collections, // 集合就更新自己
  [TreeNodeType.Task]: TreeNodeType.Tasks,
  [TreeNodeType.Visualization]: TreeNodeType.Visualizations,
};
export default function LeftPanel() {
  const projectInfo = useProjectStore((state) => state.projectInfo);
  const setProjectInfo = useProjectStore((state) => state.setProjectInfo);
  const setSimulations = useSimulationsStore((state) => state.setSimulations);
  const setTasks = useTasksStore((state) => state.setTasks);
  const setModelInfo = useModelStore((state) => state.setInfo);
  const { shapeInfo, setShapeInfo } = useShapeInfoStore();
  const [treeData, setTreeData] = useState<ITreeNode[]>([]);
  const [treeActive, setTreeAcitve] = useState<ITreeNode | null>(null);
  // 更新模型是否加载完成
  const setModelLoaded = useModelStore((state) => state.setLoaded);
  const { invokeCmd } = cmdInvoker;

  useAsyncEffect(async () => {
    const hide = showLoading();
    try {
      // 初始化整个NEXTMESH环境
      const info = await initLoad.initNextMesh(setModelLoaded);

      const tasks = await documentV2.listTasks();
      setTasks(tasks);
      info.tasks = [...tasks]
        // 因为任务列表是倒序的，所以反转任务列表
        ?.reverse()
        // 过滤掉网格的任务
        ?.filter((task) => task.type !== 'mesh');

      const results = await documentV2.listResults();
      info.results = results;

      setSimulations([...info.simulations]);
      setProjectInfo(info);

      const sInfo = initLoad.getShapeInfo();
      setShapeInfo(sInfo);
    } catch (err) {
      nmAppErrorLog('LeftPanelTree initNextMesh', err);
    } finally {
      hide();
    }
  }, []);
  /**
   * 更新treeData
   */
  useEffect(() => {
    nmAppLog('LeftPanelTree', { projectInfo });
    if (projectInfo === undefined) {
      return;
    }
    // TODO 应该根据文档信息，递归构建treeData，生成树节点的id、type等
    const td = [...LeftTreeData.treeData];
    const geometries = projectInfo.geometries?.map((g) => toLocalComponent(g));
    td[0].children = geometries ?? [];

    td[1].children = toLocalMeshControls(projectInfo.meshControl, shapeInfo);

    if (isArray(projectInfo.collections)) {
      td[2].children = toLocalCollections(projectInfo.collections, shapeInfo);
    }

    const simulations = projectInfo.simulations?.map((s) =>
      toLocalSimulation(s),
    );
    td[3].children = simulations ?? [];

    const tasksNode = projectInfo.tasks.map((s) => toLocalTask(s));
    td[4].children = tasksNode ?? [];

    const resultsNode = toLocalResults(projectInfo.results);
    const td5Children = resultsNode ? [resultsNode] : [];
    const visualizations = toLocalVisualizations(projectInfo.postprocesses);
    td5Children.push(visualizations);
    td[5].children = td5Children;
    setTreeData(td);
  }, [projectInfo, shapeInfo]);

  function addItem(data: ITreeNode, reload?: boolean) {
    const td = [...treeData];
    const parentType = childParentTypeMap[data.type];
    const nodes = td;
    // 根据type查找父节点
    const findNode = (nodes: ITreeNode[]) => {
      for (let index = 0; index < nodes.length; index++) {
        const element = nodes[index];
        if (element.type === parentType) {
          return element;
        }
        if (element.children) {
          const node = findNode(element.children);
          if (node) {
            return node;
          }
        }
      }
      return null;
    };
    const parent = findNode(nodes);
    if (!parent) {
      return;
    }
    // 更新projectInfo
    projectInfo[parentType] = projectInfo[parentType] ?? [];
    projectInfo[parentType].push(data.formData);
    setProjectInfo({ ...projectInfo });

    parent.children = parent.children ?? [];
    parent.children.push(data);
    setTreeData(td);
    if (reload) {
      onTreeNodeClick(data);
    }
  }
  function deleteItem(data: ITreeNode) {
    if (data.id === treeActive?.id) {
      setTreeAcitve(null);
    }
    const td = [...treeData];
    updateTreeNode(data, td, 'delete');
    setTreeData(td);
  }

  /**
   * 根据newNode.id，在children中找到对应的node，进行updateType（delete/replace）
   * @param newNode 新的节点
   * @param nodes 当前节点数组
   * @param updateType 更新类型 delete|replace
   */
  function updateTreeNode(
    newNode: ITreeNode,
    children: ITreeNode[],
    updateType: 'delete' | 'replace',
  ) {
    for (let index = 0; index < children.length; index++) {
      const child = children[index];
      if (child.id === newNode.id) {
        if (updateType === 'delete') {
          children.splice(index, 1);

          // 更新projectInfo
          const parentType = childParentTypeMap[child.type];
          if (parentType) {
            projectInfo[parentType]?.splice(index, 1);
            setProjectInfo({ ...projectInfo });
          }
        } else if (updateType === 'replace') {
          children[index] = newNode;

          // 更新projectInfo
          const parentType = childParentTypeMap[child.type];
          if (parentType) {
            const parent = projectInfo[parentType];
            if (isArray(parent) && index < parent.length) {
              if (parentType === TreeNodeType.Collections) {
                // 集合，直接赋值
                projectInfo[parentType] = newNode.formData;
              } else {
                projectInfo[parentType][index] = newNode.formData;
              }
            } else {
              // 不是数组，直接赋值，比如：projectInfo.preprocess是对象
              projectInfo[parentType] = newNode.formData;
            }
            setProjectInfo({ ...projectInfo });
          }
        }
        return true;
      } else if (child.children) {
        if (updateTreeNode(newNode, child.children, updateType)) {
          return true;
        }
      }
    }
    return false;
  }
  const onMenuItemClickUpdate =
    (data: ITreeNode) =>
    (newData: ITreeNode, menu?: IMenuItem, reload?: boolean) => {
      const nodeId = data.id;
      switch (menu?.value) {
        case 'addition':
          addItem(newData, reload);
          break;
        case 'delete':
          deleteItem(data);
          break;
        default:
          {
            // 其他情况，根据id进行替换
            const td = [...treeData];
            updateTreeNode(newData, td, 'replace');
            setTreeData(td);
            if (reload === true) {
              // 重新加载显示
              onTreeNodeClick(newData);
            } else {
              // 选中data节点更新后的最后一项
              // 从 td 中递归找到nodeId对应的节点
              const findNode = (td: ITreeNode[]) => {
                for (let index = 0; index < td.length; index++) {
                  const element = td[index];
                  if (element.id === nodeId) {
                    return element;
                  } else if (element.children) {
                    const node = findNode(element.children);
                    if (node) {
                      return node;
                    }
                  }
                }
                return null;
              };
              const findParent = (td: ITreeNode[]) => {
                for (let index = 0; index < td.length; index++) {
                  const element = td[index];
                  if (element.children) {
                    const node = findNode(element.children);
                    if (node) {
                      return element;
                    }
                  }
                }
                return null;
              };
              const node = findNode(td);
              if (node) {
                const children: ITreeNode[] = node.children;
                if (children?.length > 0 && menu?.value) {
                  // 选中更新后的和menuItem对应的最后一项
                  const child = children.findLast((child) =>
                    child.type?.includes(menu?.value),
                  );
                  child && onTreeNodeClick(child);
                } else {
                  onTreeNodeClick(node);
                }
              } else {
                // TODO 删除的节点找不到了，那么需要找到它的兄弟节点或父节点
                const parent = findParent(td);
                if (parent) {
                  onTreeNodeClick(parent);
                }
              }
            }
          }
          break;
      }
    };
  const onMenuItemClick = useCallback(
    async (data: ITreeNode, menuItem: IMenuItem) => {
      nmAppLog('onMenuItemClick', data, menuItem);
      try {
        if (menuItem.value === 'delete') {
          // TODO 如果是Component、网格控制和集合；直接在这里删除并更新
          // TODO 后期其他的应该也在这里删除并更新
          if (data.type === TreeNodeType.Component) {
            const index = projectInfo.geometries.findIndex(
              ({ uuid }) => uuid === data.id,
            );
            if (index >= 0) {
              await documentV2.deleteComponent(data.id);
              projectInfo.geometries.splice(index, 1);
              setProjectInfo({ ...projectInfo });
            }
            return;
          } else if (data.id.startsWith(TreeNodeType.MeshControl)) {
            const index = getFormIndex(data.id);
            const meshControl = projectInfo.meshControl;
            if (index >= 0) {
              meshControl.params[data.type].splice(index, 1);
              await documentV2.updateMeshControl(meshControl);
              setProjectInfo({ ...projectInfo });
            }
            return;
          } else if (data.id.startsWith(TreeNodeType.Collections)) {
            const index = getFormIndex(data.id);
            const collections = projectInfo.collections;
            if (index >= 0) {
              await documentV2.updateCollections(collections);
              collections.splice(index, 1);
              setProjectInfo({ ...projectInfo });
            }
            return;
          }
        }
        if (data.type === TreeNodeType.Component) {
          const component = data.formData as IComponent;
          switch (menuItem.value) {
            case 'geometryVisible':
              updateGeometriesVisible(
                component.geometryVisible ? 'hide' : 'show',
                component.objects,
              );
              projectInfo.geometries.find(
                ({ uuid }) => uuid === component.uuid,
              ).geometryVisible = !component.geometryVisible;
              setProjectInfo({ ...projectInfo });
              break;
            case 'meshVisible':
              nmAppLog('控制网格的显示/隐藏');
              break;
          }
          return;
        }
      } catch (error) {
        nmAppErrorLog('LeftPanel', 'onMenuItemClick', error);
      }
      events.emit(
        LeftPanelEventType.MenuClick,
        data,
        menuItem,
        onMenuItemClickUpdate(data),
      );
    },
    [projectInfo, treeData],
  );
  const onTreeNodeClick = useCallback(
    (data: ITreeNode) => {
      nmAppLog('LeftPanel onTreeNodeClick', data);
      if (data.type === TreeNodeType.Component) {
        setModelInfo({ name: data.label, type: data.type });
      }
      events.emit(
        LeftPanelEventType.NodeClick,
        data,
        (newData: ITreeNode, menu?: IMenuItem, reload?: boolean) => {
          const td = [...treeData];
          updateTreeNode(newData, td, 'replace');
          if (reload) {
            // 重新加载显示网格数据
            onTreeNodeClick(newData);
          }
          setTreeData(td);
        },
      );
      if (treeActive?.id === data.id) return;
      setTreeAcitve(data);
    },
    [treeData],
  );
  const onTreeNodeDoubleClick = useCallback(
    async (data: ITreeNode) => {
      const command = commandList.find((v) => v.type === data.type)?.command;
      if (command) {
        await invokeCmd(command, null, {
          id: data.id,
          formData: data.formData,
        });
      }
    },
    [treeData],
  );
  if (treeData?.length > 0) {
    return (
      <>
        <ErrorBoundary fallback={<div>树菜单加载失败</div>}>
          <Tree
            treeData={treeData}
            initActive={treeActive}
            onMenuItemClick={onMenuItemClick}
            onTreeNodeClick={onTreeNodeClick}
            onTreeNodeDoubleClick={onTreeNodeDoubleClick}
            readonly={projectInfo == null || projectInfo?.readonly}
          />
        </ErrorBoundary>
      </>
    );
  }
  return <></>;
}
